<?php

namespace Kangcg\Application\Helper;

/**
 * curl请求
 * Class Curl
 * $curl = Curl::getInstall();
 * @package MyLibs\Help
 */
class Curl
{
    const METHOD_POST = 'POST';
    const METHOD_GET = 'GET';
    public $charset = ''; //需要进行字符转换直接设置 - UTF-8

    /**
     * @param $url
     * @param string $data
     * @param string $method
     * @param bool $isUseCommon
     * @param \Closure|null $closure
     * @return bool|mixed
     */
    public function request($url, $data = '', $method = self::METHOD_GET, $isUseCommon = true, \Closure $closure = null)
    {
        $this->init();
        $curl = $this->create($url, $method, $data, $isUseCommon);
        $result = $this->execute($curl);
        !$closure or $closure($curl, $result);
        curl_close($curl);
        return $result;
    }

    /**
     * 批量執行
     * @param array $urls [ ['url'=> 'url', 'data' => [], 'method' => 'GET']]
     * @param bool $isUseCommon
     * @return mixed
     */
    public function requestMulti(array $urls, $isUseCommon = true, \Closure $closure = null)
    {
        $this->init();
        $multi = curl_multi_init();
        $curls = [];
        foreach ($urls as $key => $item) {
            $url = $item['url'];
            $data = isset($item['data']) ? $item['data'] : '';
            $method = isset($item['method']) ? $item['method'] : self::METHOD_GET;
            $curls[$key] = $this->create($url, $method, $data, $isUseCommon);
            curl_multi_add_handle($multi, $curls[$key]);
        }

        // 执行批处理句柄
        do {
            curl_multi_exec($multi, $active);
        } while ($active);

        foreach ($curls as $k => $curl) {
            $res[$k] = $this->execute($curl, false);
            !$closure or $closure($curl, $res[$k]);
            curl_multi_remove_handle($multi, $curl);//释放资源
        }

        curl_multi_close($multi);
        return $res;
    }

    /**
     * 设置单个CURL的 setopts
     * @param int $option
     * @param string $value
     * @return $this
     */
    public function setoptsValue($option, $value)
    {
        $this->_setopts[$option] = $value;
        return $this;
    }

    /**
     * 设置Ssl的文件地址
     * @param string $sslCertPath 路径
     * @param string $sslKeyPath 路径
     * @param string $sslType ssl文件类型
     * @return $this
     */
    public function setSslCert($sslCertPath, $sslKeyPath, $sslType = 'PEM')
    {
        $this->setoptsValue(CURLOPT_SSLCERTTYPE, $sslType);
        $this->setoptsValue(CURLOPT_SSLCERT, realpath('./' . $sslCertPath));
        $this->setoptsValue(CURLOPT_SSLKEY, realpath('./' . $sslKeyPath));
        return $this;
    }

    /**
     * 设置Header头部
     * @param array $header ['Content-Type' => 'application/x-www-form-urlencoded']
     * @param bool $isAppend 是否追加
     * @return $this
     */
    public function setHeader($header = [], $isAppend = true)
    {
        if ($isAppend === false) {
            $this->_headers = [];
        }

        foreach ($header as $k => $v) {
            $this->_headers[] = is_string($k) ? sprintf('%s:%s', $k, $v) : $v;
        }

        return $this;
    }

    /**
     * 模拟CURL请求头部
     * @return $this
     */
    public function setRealIp()
    {
        $ip_long = [
            ['607649792', '608174079'], //36.56.0.0-36.63.255.255
            ['1038614528', '1039007743'], //61.232.0.0-61.237.255.255
            ['1783627776', '1784676351'], //106.80.0.0-106.95.255.255
            ['2035023872', '2035154943'], //121.76.0.0-121.77.255.255
            ['2078801920', '2079064063'], //123.232.0.0-123.235.255.255
            ['-1950089216', '-1948778497'], //139.196.0.0-139.215.255.255
            ['-1425539072', '-1425014785'], //171.8.0.0-171.15.255.255
            ['-1236271104', '-1235419137'], //182.80.0.0-182.92.255.255
            ['-770113536', '-768606209'], //210.25.0.0-210.47.255.255
            ['-569376768', '-564133889'], //222.16.0.0-222.95.255.255
        ];

        $rand_key = mt_rand(0, 9);
        $ip = long2ip(mt_rand($ip_long[$rand_key][0], $ip_long[$rand_key][1]));
        $header = [
            "Connection: Keep-Alive",
            "Accept: text/html, application/xhtml+xml,
             */*",
            "Pragma: no-cache",
            "Accept-Language: zh-Hans-CN,zh-Hans;q=0.8,en-US;q=0.5,en;q=0.3",
            "User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)", 'CLIENT-IP:' . $ip, 'X-FORWARDED-FOR:' . $ip
        ];

        $this->setHeader($header);
        return $this;
    }

    /**
     * 对获取的结果 进行编码转换
     * @param string $curlInfo curl响应 content_type
     * @param string $result 要转换的字符串
     * @param string $out_charset 输出编码 -- UTF-8
     * @return string
     */
    public function mb_convert_encoding($result, $contentType, $out_charset)
    {
        $out_charset = strtoupper($out_charset);
        preg_match('/charset=(.*)/i', $contentType, $matches);
        $charset = strtoupper(isset($matches[1]) ? $matches[1] : 'UTF-8');
        if (in_array($charset, [
            'EXT/HTML',
            $out_charset
        ])) {
            return $result;
        }

        $string = mb_convert_encoding($result, $out_charset, $charset);
        return $string ? $string : $result;
    }

    /**
     * 模拟登录 保存cookie信息
     * @param string $filePath 保存cookie的文件地址
     * @return $this
     */
    public function cookieLogin($filePath)
    {
        $this->setoptsValue(CURLOPT_COOKIEJAR, $filePath);
        return $this;
    }

    /**
     * 使用cookie信息登录
     * @param string $filePath 保存cookie的文件地址
     * @return $this
     */
    public function cookieUse($filePath)
    {
        $this->setoptsValue(CURLOPT_COOKIEFILE, $filePath);
        return $this;
    }

    /**
     * 获取创建对象
     * @return resource
     */
    private function create($url, $method, $data, $isCommon)
    {
        $curl = curl_init();
        $this->setUrlSetopts($url, $method, $data);
        $setopts = $this->_setopts;
        !$isCommon or $setopts = $this->_common + $setopts;
        empty($this->_headers) or $setopts[CURLOPT_HTTPHEADER] = $this->_headers;

        curl_setopt_array($curl, $setopts);
        return $curl;
    }

    /**
     * 获取执行结果
     * @param $isOnly bool 是否是单例请求
     * @return bool|mixed
     */
    protected function execute($curl, $isOnly = true)
    {
        $result = $isOnly ? curl_exec($curl) : curl_multi_getcontent($curl); //执行操作
        if ($result === false) {
            $this->setError(curl_error($curl));
        }

        if ($this->charset) {
            $this->mb_convert_encoding($result, curl_getinfo($curl, CURLINFO_CONTENT_TYPE), $this->charset);
        }

        return $result;
    }

    public function init()
    {
        $this->_setopts = [];
    }

    /**
     * 获取CURL 请求模式
     * @param string $url
     * @param string $method
     * @param string|array $data
     * @return mixed
     */
    private function setUrlSetopts($url, $method, $data)
    {
        $url = trim($url, '?');
        $this->_setopts[CURLOPT_URL] = $url;
        if (strpos($url, 'https:') === 0) {
            $this->_setopts[CURLOPT_SSL_VERIFYPEER] = false;// 对认证证书来源的检查
            $this->_setopts[CURLOPT_SSL_VERIFYHOST] = false;// 从证书中检查SSL加密算法是否存在
            $this->_setopts[CURLOPT_SSLVERSION] = true;//CURL_SSLVERSION_TLSv1
        }

        switch ($method) {
            case self::METHOD_POST :
                $this->_setopts[CURLOPT_POST] = true;
                $this->_setopts[CURLOPT_POSTFIELDS] = $data;
                break;
        }
    }

    private function setError($error)
    {
        $this->_error = $error;
    }

    public function getError()
    {
        return $this->_error;
    }

    /**
     * 获取请求实例对象
     * @return Curl
     */
    public static function getInstall()
    {
        if (self::$_install === null) {
            self::$_install = new self();
        }

        return self::$_install;
    }

    //公共的setopts
    private $_common = [
        CURLOPT_HEADER => 0, // 显示返回的Header区域内容
        CURLOPT_RETURNTRANSFER => 1, // 获取的信息以文件流的形式返回
        CURLOPT_FOLLOWLOCATION => true, // 使用自动跳转
        CURLOPT_AUTOREFERER => true, //自动设置Referer
        CURLOPT_TIMEOUT => 10,// 设置超时限制防止死循环
    ];

    private function __construct()
    {
    }

    private static $_install = null;
    private $_setopts = [];
    private $_headers = [];
    private $_error;
}
